/* Zatok - язык программирования текстовых игр
 * (С) 2010 - 2012 Меркулов Матвей Юрьевич
 * Код лицензирован под лицензией GNU LGPL
 * Вы можете прочитать текст лицензии по адресу:
 * http://www.gnu.org/copyleft/lesser.html
 * или прочитать русский перевод по адресу:
 * http://ru.wikisource.org/wiki/GNU_Lesser_General_Public_License
 */

package base;

import base.intrface.CMenu;
import base.module.intrface.CSetFrame;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;

public class CGraphics {
	public static Component Component;
	public static CTitleSetter TitleSetter;
	public static boolean DrawingInProgress, IsBusy = true;
	public static int MouseX, MouseY;

	public static BufferedImage[][] MainFrame;
	public static BufferedImage Texture;
	public static Color BackgroundColor = Color.WHITE;
	public static Color TextColor = Color.BLACK;
	public static Color LinkColor = Color.BLUE;
	public static Color SelectedLinkColor = Color.RED;
	public static Font TextFont;
	private static FontMetrics TextFontMetrics;
	public static int TextDY;

	public static BufferedImage[][] MenuFrame;
	public static BufferedImage MenuTexture;
	public static Color MenuBackgroundColor;
	public static Color MenuTextColor = Color.BLACK;
	public static Color MenuSelectionColor = Color.BLACK;
	public static Color MenuSelectionTextColor = Color.WHITE;
	public static Font MenuFont;
	private static FontMetrics MenuFontMetrics;
	public static LinkedList<CMenu> Menus = new LinkedList<CMenu>();
	public static int MenuTextDY;

	public static BufferedImage ImageBuffer = null;
	public static BufferedImage DrawingImage = null;
	public static HashMap<String, BufferedImage> Images = new HashMap<String, BufferedImage>();
	
	public static int SliderWidth;
	public static BufferedImage[][] SliderField;
	public static BufferedImage[][] SliderBody;
	
	public static BufferedImage[][] DividerFrame;
	public static BufferedImage CentralDivider;

	
	
	public static BufferedImage[][] SetFrame( String Filename, int LeftBorder, int TopBorder, int RightBorder, int BottomBorder ) {
		BufferedImage FrameImage = CGraphics.LoadImage( Filename );
		
		if ( LeftBorder + RightBorder > FrameImage.getWidth() ) Error( "Ширина крайних блоков должна быть меньше ширины изображения" );
		if ( TopBorder + BottomBorder > FrameImage.getHeight() ) Error( "Высота крайних блоков должна быть меньше высоты изображения" );

		int FrameX = 0, FrameY = 0, FrameWidth = 0, FrameHeight = 0;
		BufferedImage[][] Frame = new BufferedImage[ 3 ][ 3 ];
		
		for ( int YN = 0; YN < 3; YN++ ) {
			for ( int XN = 0; XN < 3; XN++ ) {
				switch ( XN ) {
					case 0:
						FrameX = 0;
						FrameWidth = LeftBorder;
						break;
					case 1:
						FrameX = LeftBorder;
						FrameWidth = FrameImage.getWidth() - LeftBorder - RightBorder;
						break;
					case 2:
						FrameX = FrameImage.getWidth() - RightBorder;
						FrameWidth = RightBorder;
						break;
				}
				switch ( YN ) {
					case 0:
						FrameY = 0;
						FrameHeight = TopBorder;
						break;
					case 1:
						FrameY = TopBorder;
						FrameHeight = FrameImage.getHeight() - TopBorder - BottomBorder;
						break;
					case 2:
						FrameY = FrameImage.getHeight() - BottomBorder;
						FrameHeight = BottomBorder;
						break;
				}
				if( FrameWidth > 0 && FrameHeight > 0 ) {
					Frame[ XN ][ YN ] = new BufferedImage( FrameWidth, FrameHeight, BufferedImage.TYPE_INT_ARGB );
					Graphics FrameGraphics = Frame[ XN ][ YN ].getGraphics();
					FrameGraphics.drawImage( FrameImage, -FrameX, -FrameY, null );
				}
			}
		}

		return Frame;
    }


	public static void DrawFrame( Graphics g, BufferedImage[][] FrameImages, int X, int Y, int Width, int Height ) {
		if( FrameImages == null ) return;
		
		int FrameX = 0, FrameY = 0, FrameWidth = 0, FrameHeight = 0;
		for ( int YN = 0; YN < 3; YN++ ) {
			for ( int XN = 0; XN < 3; XN++ ) {
				if( FrameImages[ XN ][ YN ] == null ) continue;
				FrameWidth = FrameImages[ XN ][ YN ].getWidth();
				FrameHeight = FrameImages[ XN ][ YN ].getHeight();
				switch ( XN ) {
					case 0:
						FrameX = X;
						break;
					case 1:
						FrameX = X;
						FrameWidth = Width;
						if( FrameImages[ 0 ][ 1 ] != null ) {
							FrameX += FrameImages[ 0 ][ 1 ].getWidth();
							FrameWidth -= FrameImages[ 0 ][ 1 ].getWidth();
							if( FrameImages[ 2 ][ 1 ] != null ) FrameWidth -= FrameImages[ 2 ][ 1 ].getWidth();
						}
						break;
					case 2:
						FrameX = X + Width - FrameWidth;
						break;
				}
				switch ( YN ) {
					case 0:
						FrameY = Y;
						break;
					case 1:
						FrameY = Y;
						FrameHeight = Height;
						if( FrameImages[ 1 ][ 0 ] != null ) {
							FrameY += FrameImages[ 1 ][ 0 ].getHeight();
							FrameHeight -= FrameImages[ 1 ][ 0 ].getHeight();
							if( FrameImages[ 1 ][ 2 ] != null ) FrameHeight -= FrameImages[ 1 ][ 2 ].getHeight();
						}
						break;
					case 2:
						FrameY = Y + Height - FrameHeight;
						break;
				}
				g.drawImage( FrameImages[ XN ][ YN ], FrameX, FrameY, FrameWidth, FrameHeight, null );
			}
		}
	}



	public static BufferedImage LoadImage( String Filename ) {
		Filename = CBase.GetFilename( CProject.ProjectDir, Filename );
		BufferedImage Image = Images.get( Filename );
		if ( Image == null ) {
			try {
				Image = ImageIO.read( new File( Filename ) );
			} catch ( IOException ex ) {
				Logger.getLogger( CSetFrame.class.getName() ).log( Level.SEVERE, null, ex );
			}
			Images.put( Filename, Image );
		}
		return Image;
	}



	public static int GetCanvasWidth() {
		return Component.getWidth();
	}



	public static int GetCanvasHeight() {
		return Component.getHeight();
	}



	public static void Repaint() {
		CGraphics.IsBusy = false;
		Component.repaint();
	}



	public static void Init(){
		CGUI.CurrentOutputWindow = CGUI.InformationWindow;
		if ( TextFont == null ) TextFont = Component.getFont();
		TextFontMetrics = Component.getFontMetrics( TextFont );
		if ( MenuFont == null ) MenuFont = Component.getFont();
		MenuFontMetrics = Component.getFontMetrics( MenuFont );
		TextDY = GetTextFontHeight() - 3;
		MenuTextDY = GetMenuFontHeight() - 3;
	}



	public static void SetTitle( String Title ) {
		TitleSetter.SetTitle( Title );
	}



	public static int GetTextFontHeight(){
		if ( TextFontMetrics == null ) return 0;
		return TextFontMetrics.getHeight();
	}



	public static int GetCharWidth( char Character ) {
		if ( TextFontMetrics == null ) return 0;
		return TextFontMetrics.charWidth( Character );
	}



	public static int GetMenuFontHeight() {
		if ( MenuFontMetrics == null ) return 0;
		return MenuFontMetrics.getHeight();
	}



	public static int GetMenuTextWidth( String Text ) {
		if ( MenuFontMetrics == null ) return 0;
		return MenuFontMetrics.stringWidth( Text );
	}



	@SuppressWarnings( "CallToThreadYield" )
	public static void WaitToCompleteDrawing() {
		while( DrawingInProgress ) Thread.yield();
		IsBusy = true;
	}



	public static void ShowInfo( String Title, String Message ) {
		JOptionPane.showMessageDialog( Component, Message, Title, JOptionPane.INFORMATION_MESSAGE );
	}



	public static void SetBaseImage( int Width, int Height ) {
		ImageBuffer = new BufferedImage( Width, Height, BufferedImage.TYPE_4BYTE_ABGR );
	}



	public static void SetBaseImage( String Filename ) {
		if( Filename.isEmpty() ) {
			ImageBuffer = null;
		} else {
			BufferedImage Image = LoadImage( Filename );
			if( Image == null ) Error( "Изображение " + Filename + " не найдено." );
			ImageBuffer = new BufferedImage( Image.getWidth(), Image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR );
			ImageBuffer.createGraphics().drawImage( Image, 0, 0, null );
		}
	}



	public static int GetBaseImageWidth() {
		return ImageBuffer.getWidth();
	}



	public static int GetBaseImageHeight() {
		return ImageBuffer.getHeight();
	}




	public static void DrawImage( String Filename, int X, int Y, boolean Centered, double Scale ) {
		BufferedImage Image = LoadImage( Filename );
		Graphics2D Graphics = ImageBuffer.createGraphics();
		int Width = (int) ( Image.getWidth() * Scale );
		int Height = (int) ( Image.getHeight() * Scale );
		if ( Centered ) {
			Graphics.drawImage( Image, X - Width / 2, Y - Height / 2, Width, Height, null );
		} else {
			Graphics.drawImage( Image, X, Y, Width, Height, null );
		}
	}

	
	
	public static void UpdateImages() {
		boolean Rearrange = true;
		if( ImageBuffer == null ) {
			DrawingImage = null;
		} else {
			Rearrange = ( DrawingImage == null || DrawingImage.getWidth() != ImageBuffer.getWidth() || DrawingImage.getHeight() != ImageBuffer.getHeight() );
			DrawingImage = new BufferedImage( ImageBuffer.getWidth(), ImageBuffer.getHeight(), BufferedImage.TYPE_4BYTE_ABGR );
			DrawingImage.createGraphics().drawImage( ImageBuffer, 0, 0, null );
		}
		if( Rearrange ) CGUI.Rearrange( true );
		Component.repaint();
	}



	public static void Error( String Message ){
		PrintWriter PWriter = null;
		try {
			PWriter = new PrintWriter( new FileWriter( "error.log" ) );
		} catch ( IOException ex ) {
			Logger.getLogger( CProject.class.getName() ).log( Level.SEVERE, null, ex );
		}
		PWriter.print( Message );
		PWriter.close();
		IsBusy = false;
		JOptionPane.showMessageDialog( Component, Message, "Ошибка", JOptionPane.ERROR_MESSAGE );
		System.exit( 1 );
	}



	@SuppressWarnings( "CallToThreadYield" )
	static void Draw( Graphics G ) {
		while( IsBusy ) Thread.yield();
		
		if( CGUI.MaxWidth != 0 ) CApplication.Frame.setSize( Math.min( CApplication.Frame.getWidth(), CGUI.MaxWidth ),
				Math.min( CApplication.Frame.getHeight(), CGUI.MaxHeight ) );
		
		DrawingInProgress = true;

		( (Graphics2D) G ).setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );

		G.setColor( BackgroundColor );
		G.drawRect( 0, 0, GetCanvasWidth(), GetCanvasHeight() );

		G.setColor( TextColor );
		if ( Texture != null ) {
			for ( int Y = 0; Y < GetCanvasHeight(); Y += Texture.getHeight() ) {
				for ( int X = 0; X < GetCanvasWidth(); X += Texture.getWidth() ) {
					G.drawImage( Texture, X, Y, null );
				}
			}
		}

		DrawFrame( G, MainFrame, 0, 0, GetCanvasWidth(), GetCanvasHeight() );
		
		if( CGUI.InformationWindow.Height > 0 ) {
			DrawFrame( G, DividerFrame, 0, CGUI.InformationWindow.Y - CGUI.WindowsMargin, GetCanvasWidth(), CGUI.WindowsMargin );
			if( CentralDivider != null ) G.drawImage( CentralDivider, ( GetCanvasWidth() - CentralDivider.getWidth() ) / 2, CGUI.InformationWindow.Y - CGUI.WindowsMargin, null );
		}

		if ( DrawingImage != null ) {
			G.drawImage( DrawingImage, ( CGraphics.GetCanvasWidth() - CGUI.ImageWidth ) / 2, CGUI.Margins[ 2 ], CGUI.ImageWidth, CGUI.ImageHeight, null );
		}

		CGUI.GadgetUnderMouse = null;
		CGUI.DescriptionWindow.Display( G );
		CGUI.InformationWindow.Display( G );

		for ( CMenu Menu: CGraphics.Menus ) {
			Menu.Draw( G, 0, 0 );
		}

		DrawingInProgress = false;
    }


	
	public static void DrawSlider( Graphics G, CWindow Window ) {
		if( Window.Height == 0 ) return;
		double K = 1.0 * Window.ViewHeight / Window.Height;
		int Height;
		if( Window.ViewHeight >= Window.Height ) {
			Window.YShift = Window.ViewHeight;
			Height = Window.ViewHeight;
		} else {
			Window.YShift = Math.max( Math.min( Window.YShift, Window.Height ), Window.ViewHeight );
			Height = (int) ( Window.ViewHeight * K ) + 1;
		}
		int X = Window.X + Window.Width;
		CGraphics.DrawFrame( G, SliderField, X, Window.Y, SliderWidth, Window.ViewHeight );
		CGraphics.DrawFrame( G, SliderBody, X, Window.Y + (int) ( ( Window.YShift - Window.ViewHeight ) * K ), SliderWidth, Height );
	}
}